using System;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.ServiceProcess;
using System.Windows.Forms;

using Pegasus.Collections.Generic;
using Pegasus.Diagnostics;
using Pegasus.Log4Net;
using Pegasus.Windows.Forms;

namespace Pegasus.ServiceProcess
{
	/// <summary>
	/// Base class for system services.
	/// </summary>
	public class ServiceExecutable : ServiceBase
	{
		// Local Instance Values
		private ILog m_log = LogManager.GetLogger( typeof( ServiceExecutable ) );
		private bool m_asService = true;
		private Type m_serviceInstallerType = null;

		/// <summary>
		/// Initializes a new instance of the <see cref="T:ServiceExecutable"/> class.  
		/// This constructor is no longer supported.
		/// </summary>
		[Obsolete( "Use ServiceExecutable( string serviceName ) constructor", true )]
		public ServiceExecutable() : 
			this( "UnknownPegasusService" )
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="T:ServiceExecutable"/> class.
		/// </summary>
		/// <param name="serviceName">Name of the service.</param>
		public ServiceExecutable( string serviceName )
		{
			m_log.DebugFormat( "ServiceExecutable( serviceName = {0} )", serviceName );

			// Check Parameters
			ParamCode.AssertNotEmpty( serviceName, "serviceName" );

			ServiceName = serviceName;

			// If the service is started under a debugger then run in app mode.
			m_asService = !Debugger.IsAttached;

			MsgBox.ErrorCaption = serviceName + " Error";
			MsgBox.NonErrorCaption = serviceName;
		}

		/// <summary>
		/// Gets or sets a value indicating whether to run the system as a service or application (debug mode).
		/// </summary>
		/// <value><c>true</c> if run as service; otherwise, <c>false</c>.</value>
		public bool RunAsService
		{
			get
			{
				return m_asService;
			}

			set
			{
				m_asService = value;
			}
		}

		/// <summary>
		/// Gets or sets the type for the service installer class.
		/// </summary>
		/// <value>The service installer.</value>
		public Type ServiceInstallerType
		{
			get
			{
				return m_serviceInstallerType;
			}

			set
			{
				m_serviceInstallerType = value;
			}
		}
		
		/// <summary>
		/// Runs the service.
		/// </summary>
		/// <param name="args">The args.</param>
		public void RunService( string[] args )
		{
            if( args != null && args.Length > 0 )
            {
                m_log.DebugFormat( "ServiceExecutable:RunService( args = {0} )", args );
            }

			foreach( string arg in args )
			{
				switch( arg.ToLower() )
				{
					case "/app":
						m_asService = false;
						break;

					case "/install":
						InstallService( true );
						return;

					case "/uninstall":
						InstallService( false );
						return;

					case "/help":
					case "-help":
					case "/h":
					case "-h":
					case "/?":
					case "-?":
						PrintHelp();
						return;
				}
			}

			if( m_asService )
			{
				m_log.Debug( "ServiceExecutable:RunService: Running as a service." );
				ServiceBase.Run( this );
			}
			else
			{
				m_log.Debug( "ServiceExecutable:RunService: Running as a application." );
				OnStart( args );

				FormServiceApplication dialog = new FormServiceApplication( this );
				Application.Run( dialog );

				if( dialog.DialogResult == DialogResult.OK )
				{
					OnStop();
				}

				Application.Exit();
			}
		}

		/// <summary>
		/// Determines whether the service has be installed or not.
		/// </summary>
		/// <returns>
		/// 	<c>true</c> if the service is installed; otherwise, <c>false</c>.
		/// </returns>
		public bool IsServiceInstalled()
		{
			ServiceController[] services = ServiceController.GetServices();
			foreach( ServiceController service in services )
			{
				if( service.ServiceName == ServiceName )
				{
					return true;
				}
			}

			return false;
		}

		/// <summary>
		/// Fires the on pause command in debug/application mode.
		/// </summary>
		/// <remarks>
		/// This method should only be called by the FormServiceApplication class.
		/// </remarks>
		internal void FireOnPause()
		{
			OnPause();
		}

		/// <summary>
		/// Fires the on continue command in debug/application mode.
		/// </summary>
		/// <remarks>
		/// This method should only be called by the FormServiceApplication class.
		/// </remarks>
		internal void FireOnContinue()
		{
			OnContinue();
		}

		/// <summary>
		/// Fires the on shutdown.
		/// </summary>
		internal void FireOnShutdown()
		{
			OnShutdown();
		}

		/// <summary>
		/// Installs or Uninstalls the service.
		/// </summary>
		/// <param name="doInstall">if set to <c>true</c> to do the install; otherwise unistall the service.</param>
		protected virtual void InstallService( bool doInstall )
		{
			try
			{
				if( m_serviceInstallerType != null )
				{
					ConstructorInfo defaultConstructor = m_serviceInstallerType.GetConstructor( new Type[ 0 ] );
					Installer installer = (Installer) defaultConstructor.Invoke( new object[ 0 ] );
					if( installer != null )
					{
						InstallContext context = new InstallContext();
						installer.Context = context;
						context.Parameters[ "assemblypath" ] = installer.GetType().Assembly.Location;

						DictionaryApplicationData<string, object> stateTable = new DictionaryApplicationData<string, object>( ServiceName + "InstallInfo" );

						if( doInstall )
						{
							if( !IsServiceInstalled() )
							{
								stateTable.Clear(); // Make sure the table is clear.
								installer.Install( stateTable );

								// Save the state data for the uninstall
								stateTable.Save();

								MsgBox.InfoBox( "Service installed sucessfully." );
							}
							else
							{
								MsgBox.ErrorBox( "The Service is already installed." );
							}
						}
						else
						{
							if( IsServiceInstalled() )
							{
								installer.Uninstall( stateTable );

								// Delete the file because we are done with it.
								File.Delete( stateTable.Filename );

								MsgBox.InfoBox( "Service uninstalled sucessfully." );
							}
							else
							{
								MsgBox.ErrorBox( "The Service is not installed." );
							}
						}
					}
					else
					{
						MsgBox.ErrorBox( "Could not construct the installer class." );
					}
				}
				else
				{
					ExitCode = -1;
					MsgBox.ErrorBox( "There is no service installer type registered." );
				}
			}
			catch( Exception e )
			{
				ExitCode = -1;
				MsgBox.ErrorBox( "Error durning operation: " + e.Message );
			}
		}

		/// <summary>
		/// Prints the commandline help text.
		/// </summary>
		protected virtual void PrintHelp()
		{
			string USAGE_TEXT =
				"Usage: {0} [options]\n\n" +
				"Options:\n\n" +
				"/app - Launches the service as a windows application.\n\n" +
				"/install - Installs the Windows service.\n\n" +
				"/uninstall - Uninstalls the Windows service.\n\n" +
				"/? | /help - Shows this help text for command line options.";

			string exeName = Path.GetFileName( Assembly.GetEntryAssembly().CodeBase );

			MsgBox.InfoBox( string.Format( USAGE_TEXT, exeName ) );
		}
	}
}
